const Controller = require('egg').Controller;

const nowTime = Date.now();
const Web3 = require('web3');
const HDWalletProvider = require('truffle-hdwallet-provider');
const Decimal = require('decimal.js');
const BigNumber = require('bignumber.js');
// var web3 = new Web3();
// web3.setProvider(new Web3.providers.HttpProvider("https://rinkeby.infura.io/v3/ef8cd40931114dabb4032fc0314a4d55"));
const mnemonic = "winner weird snack cry today penalty isolate joy secret turn jealous wagon"
const url = "https://rinkeby.infura.io/v3/ef8cd40931114dabb4032fc0314a4d55";
const web3 = new Web3(new HDWalletProvider(mnemonic, url));

const timeout = 5;
function toInt(str) {
    if (typeof str === 'number') return str;
    if (!str) return str;
    return parseInt(str, 10) || 0;
}
// 创建订单
const createRule = {
    uuid: { type: 'string' },
    actionSelector: { type: 'string' },
    userTimeStamp: { type: 'string' },
    amount: { type: 'string' },
    sign: { type: 'string' },
    actionAddress: { type: 'string' },
    userAddress: { type: 'string' },
    isBuy: { type: 'string' }
};
// 提现
const cashRule = {
    uuid: { type: 'string' },
    actionSelector: { type: 'string' },
    // parameter:  { type: 'string' },
    userTimeStamp: { type: 'string' },
    // srvTimestamp: { type: 'string' },
    amount: { type: 'string' },
    sign: { type: 'string' },
    // actionAddress: { type: 'string' },
    // accountId: { type: 'string' },
    // blockNum: { type: 'string' },
    cash: { type: 'string' }
};
const listRule = {
    userAddress: {type: 'string'}
};

// 准备 定时发送用的
const sendRule = {
    ifFail: {type: 'string'}
};
// 合约rest
const contractRule = {
    accountAddr: {type: 'string'},
    actionCtrAddr: {type: 'string'},
    parameter: {type: 'string'},
    startTime: {type: 'string'},
    endTime: {type: 'string'},
};
// 前段买涨买跌统计
const isBuyCountRule = {
    openTime: {type: 'string'},
    closeTime: {type: 'string'},
}


class ActionController extends Controller {
    /**
     * 前端列表显示
     * 参数：limit offset userAddress 
     */
    async index() {
        // const ctx = this.ctx;
        const { ctx, service } = this;
        ctx.status = 200;
        ctx.body = await service.actions.index();
    }
    /**
     * 冻结余额 = 下单冻结 + 提现冻结
     * 参数 userAddress
     */ 
    async blockedBalance() {
        // const ctx = this.ctx;
        const { ctx, service } = this;
        ctx.status = 200;
        let res = await service.actions.blockedBalance();
        if(res.status_code == '422') {
            ctx.status = 422; 
        }else {
            ctx.status = 200; 
        }
        ctx.body = res;
        return;
    }
    /** 
     * 当前买涨/买跌的数量(前端)
     * 涨1 | 跌2
     * */ 
    async isBuyCount() {
        const { ctx, service } = this;
        ctx.status = 200;
        ctx.body = await service.actions.isBuyCount();
        return;
    }

    /**
     * 下订单和提现 在一起
     * 1、验证提交的数据是否为空
     * 2、验证用户是否存在 // 下面验证了签名这一步就不用了
     * 3、验证随机数是否重复
     * 4、下单是否超时
     * 5、从accounts 表根据userAddress拿到account id 赋值给 actions 表的accountId
     * 5、签名是否是这个用户
     * 6、用户合约余额 >= 本地balance - freezeBalance
     * 6、accounts余额什么的都够，生成订单的时候报错用了事务
     * 7、看看这一轮是否同时下了买涨|买跌，下的涨也下了跌那就是isMore 就是true(1)
     * ------------------ 有这个符号的都是需要去合约拿值的 ------------------
     */ 
    async create() {

        // const ctx = this.ctx;
        const { ctx, service } = this;
        // console.log(await service.abi.WithDraw_CtrAddr())
        // ctx.body = await service.abi.WithDraw_CtrAddr();
        // return;

        // web3 = new Web3(new HDWalletProvider(mnemonic, url));
        // 提现 await service.actions.
        // let contract = new web3.eth.Contract(await service.abi.WithDraw_ABI(), await service.abi.WithDraw_CtrAddr());
        let serviceCreate = await service.create.create();
      
        if(serviceCreate.status_code == 422) {
            ctx.status = 422
            ctx.body = serviceCreate
            return
        }
      
        let amount = web3.utils.fromWei(serviceCreate.amount, 'ether');
        let userAddress = ctx.request.body.userAddress
        ctx.request.body.amount =  amount;
        ctx.request.body.sign = serviceCreate.sign;
        ctx.request.body.isBuy = serviceCreate.isBuy;
        // ctx.request.body.amount = web3.utils.fromWei(amount, 'ether');
        //serviceCreate.amount;
        ctx.request.body.accountId = serviceCreate.accountId;
        ctx.request.body.srvTimestamp = serviceCreate.srvTimestamp;
        ctx.request.body.userTimeStamp = serviceCreate.userTimeStamp;
        ctx.request.body.actionSelector = serviceCreate.actionSelector;
        ctx.request.body.actionAddress = ctx.request.body.actionAddress;
        ctx.request.body.encodeParameters = serviceCreate.encodeParameters;

        const conn = await this.app.mysql.beginTransaction();
   
        // 下单的金额够不够用 不够直接catch，够了then的时候形成订单失败也会回滚 
        try {
            let updateRes = await conn.query(`UPDATE accounts SET freezeBalance=freezeBalance+${amount} WHERE userAddress='${userAddress}' and balance-freezeBalance>='${amount}'`)
            // await ctx.model.Action.create
            if(updateRes.affectedRows == 1) {
                const { uuid, actionSelector,userTimeStamp, srvTimestamp, amount, sign, actionAddress, accountId, isBuy, encodeParameters } = ctx.request.body;
                const action = await ctx.model.Action.create({ uuid, actionSelector,userTimeStamp, srvTimestamp, amount, sign, actionAddress, accountId, isBuy, encodeParameters });
                await conn.commit(); // 提交事务
                ctx.status = 201;
                return;
            }else {
                await conn.rollback(); // 一定记得捕获异常后回滚事务！！
                ctx.status = 422;
                ctx.body = {'status_code':422, 'msg': err};
            }
        } catch(err) {
            await conn.rollback(); // 一定记得捕获异常后回滚事务！！
            ctx.status = 422;
            ctx.body = {'status_code':422, 'msg': err};
        }
       
    }


    async cashCreate() {

        // const ctx = this.ctx;
        const { ctx, service } = this;
        // console.log(await service.abi.WithDraw_CtrAddr())
        // ctx.body = await service.abi.WithDraw_CtrAddr();
        // return;

        // web3 = new Web3(new HDWalletProvider(mnemonic, url));
        // 提现 await service.actions.
        // let contract = new web3.eth.Contract(await service.abi.WithDraw_ABI(), await service.abi.WithDraw_CtrAddr());
        let serviceCreate = await service.create.create();
      
        if(serviceCreate.status_code == 422) {
            ctx.status = 422
            ctx.body = serviceCreate
            return
        }
      
        let amount = web3.utils.fromWei(serviceCreate.amount, 'ether');
        let userAddress = ctx.request.body.userAddress
        ctx.request.body.amount =  amount;
        ctx.request.body.sign = serviceCreate.sign;
        ctx.request.body.isBuy = serviceCreate.isBuy;
        // ctx.request.body.amount = web3.utils.fromWei(amount, 'ether');
        //serviceCreate.amount;
        ctx.request.body.accountId = serviceCreate.accountId;
        ctx.request.body.srvTimestamp = serviceCreate.srvTimestamp;
        ctx.request.body.userTimeStamp = serviceCreate.userTimeStamp;
        ctx.request.body.actionSelector = serviceCreate.actionSelector;
        ctx.request.body.actionAddress = ctx.request.body.actionAddress;
        ctx.request.body.encodeParameters = serviceCreate.encodeParameters;

        const conn = await this.app.mysql.beginTransaction();
   
        // 下单的金额够不够用 不够直接catch，够了then的时候形成订单失败也会回滚 
        try {
            let updateRes = await conn.query(`UPDATE accounts SET freezeBalance=freezeBalance+${amount} WHERE userAddress='${userAddress}' and balance-freezeBalance>='${amount}'`)
            // await ctx.model.Action.create
            if(updateRes.affectedRows == 1) {
                const { uuid, actionSelector,userTimeStamp, srvTimestamp, amount, sign, actionAddress, accountId, isBuy, encodeParameters } = ctx.request.body;
                const action = await ctx.model.Action.create({ uuid, actionSelector,userTimeStamp, srvTimestamp, amount, sign, actionAddress, accountId, isBuy, encodeParameters });
            
                let accountRow = await ctx.model.Account.findById(action.accountId);
                let uuid = action.uuid;
                let userAddr = accountRow.userAddress;
                let withdrawAmount = action.amount;
                let sign = action.sign;
                // 合约的
                let accounts = await web3.eth.getAccounts();
                let result = await contract.methods.ActionCallBack_WithDraw(uuid,userAddr,withdrawAmount,sign).send({
                    from: accounts[0],
                })
                console.log(result)
            }else {
                await conn.rollback(); // 一定记得捕获异常后回滚事务！！
                ctx.status = 422;
                ctx.body = {'status_code':422, 'msg': err};
            }
        } catch(err) {
            await conn.rollback(); // 一定记得捕获异常后回滚事务！！
            ctx.status = 422;
            ctx.body = {'status_code':422, 'msg': err};
        }
       
    }
    // ifFail 成功1 | 失败0 失败的要再次发送 ，isSettled 是否已结算 未结算0 | 已结算1
    async sendContract() {
        const ctx = this.ctx;
        //
        // ifFail 就是发送失败或者没有发送的
        const query = {
            where: {
                srvTimestamp: {
                    $between: [toInt(ctx.query.startTime),toInt(ctx.query.endTime)]
                }
            }     
        };

        const actions = await ctx.model.Action.findAll(query);
        
        // 数据库没有一条符合条件的轮询，直接结束
        if (actions.length == 0) {
            ctx.status = 422;
            ctx.body = {'status_code':422, 'msg': '没有单子要传！'};
            return;
        }
        // 
        let web3 = new Web3(new HDWalletProvider(mnemonic, url));
        // ctx.status = 201;
        // ctx.body = actions;
        // return;
        let contract2 = new web3.eth.Contract(await service.abi.Order_ABI(), await service.abi.Order_CtrAddr());
        // console.log(contract2.methods)
        actions.forEach(async function(row)  {
            let nonce = 0;
            // 只有超过指定时间才发送到前段去
            // 获取用户地址
            const accountRow = await ctx.model.Account.findOne({
                where: {
                    id: 8
                }
            });
            let uuid = "0xfea3eaa111b347e621dbfe36b695e5f1b8de87d25614bde36554a296a32410";
            let userAddr = accountRow.userAddress;
            let tradeAmount = "1000000000000000000000000";
            let isBuy = true;
            let srvTimestamp = row.srvTimestamp;
            let sign = row.sign;
            let accounts = await web3.eth.getAccounts();
            let result1 = await contract2.methods.Nosign_ActionCallBack_SettleOrder(uuid,userAddr,tradeAmount,isBuy, srvTimestamp,sign).send({
                from: accounts[0],
                gas:"5000000",
            })
                
        });
        ctx.status = 201;
        ctx.body = 1111;


    }

    /**
     * 智能合约rest
     * 先判断 accounts 是否有这个 accountAddr 有形成一个数组，然后判断actions 是否在这个数组中
     *  */
  
    async contractRes() {
        const ctx = this.ctx;
        
        ctx.validate(contractRule, ctx.query);
        let actionRes = await ctx.model.Account.findAll({
            where: {
                accountAddress: ctx.query.accountAddr
            }
        });
       
        // 符合 accountAddr 存一个数组
        let accountIdArr = [];
        actionRes.forEach(async function (row) {
            accountIdArr.push(row.id);
        });
        
        // 把发送失败的要再次去发送的
        let ifFail = 0;
        // 
        const query = {
            where: {
                actionAddress: ctx.query.actionCtrAddr,
                parameter: ctx.query.parameter,
                ifFail: ifFail,  
                accountId: accountIdArr,
                srvTimestamp: {
                    $between: [toInt(ctx.query.startTime),toInt(ctx.query.endTime)]
                }
            },
        };

        const actions = await ctx.model.Action.findAll(query);
        let addAmount = 0;
        actions.forEach(async function (row) {
            addAmount = new BigNumber(addAmount).plus(new BigNumber(row.amount));
        });

        ctx.status = 200;
        ctx.body = { 'status_code': 200, 'currBalance': addAmount };
        return;
    }
    // 监听用的
    async accountBalance() {
        const ctx = this.ctx;
        let userAddress = ctx.query.userAddrDeposit;
        let balance = ctx.query.amountDeposit;
        let event = ctx.query.event;
        web3 = new Web3(new HDWalletProvider(mnemonic, url));
        balance =  web3.utils.fromWei(balance, 'ether');//amount;
       
        // 没有就创建，有就更新balance
        await ctx.model.Account.findOrCreate({
            where: {userAddress: userAddress}, 
            defaults: {accountAddress: 123456789, userAddress: userAddress, balance: balance, freezeBalance: 0}
          })
          .spread(async (action, created) => {
            if(created === false) {
                const conn = await this.app.mysql.beginTransaction();
                // 下单的金额够不够用 不够直接catch，够了then的时候形成订单失败也会回滚 
                // 判断是加还是数据库是入金 分奖池 还是提现 
                if(event == "Withdraw") { 
                    await conn.query(`UPDATE accounts SET balance=balance-${balance} WHERE userAddress='${userAddress}'`).then( async result => {
                        await conn.commit(); // 提交事务
                        ctx.status = 201;
                        ctx.body = {'status_code':201, 'msg': "提现 成功！"};
                    })
                }else {
                    await conn.query(`UPDATE accounts SET balance=balance+${balance} WHERE userAddress='${userAddress}'`).then( async result => {
                        await conn.commit(); // 提交事务
                        ctx.status = 201;
                        ctx.body = {'status_code':201, 'msg': "balance 成功！"};
                    })
                }
            }
          })
        ctx.status = 201;
        ctx.body = { 'status_code': 201, 'data': '入金成功！' };
    }

    // 监听修改
    async updateAction() {
        const ctx = this.ctx;
        // let uuid = uuid;
        let constractUuid = ctx.query.uuid; // 这个应该是合约返回的值
        let allocation = ctx.query.allocation;
        const ActionCastRes = await ctx.model.Action.findOne({
            where: {
                uuid: constractUuid
            }
        });
        
        // 合约返回的时候还有一个特殊的值 用于判断是否成功 成功1 失败是2
        if(allocation == true) {
            await ActionCastRes.update({ ifFail:1 }); // ifFail 1 成功， 0没有提交 2 失败
        }else {
            await ActionCastRes.update({ ifFail:2 });
        }
        ctx.status = 201;
        ctx.body = { 'status_code': 201, 'data': '订单修改成功！' };
    }
  
}

module.exports = ActionController;